package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JWTUtils;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.common.utils.RegexUtil;
import com.example.lotterysystem.common.utils.SecurityUtil;
import com.example.lotterysystem.controller.param.ShortMessageLoginParam;
import com.example.lotterysystem.controller.param.UserLoginParam;
import com.example.lotterysystem.controller.param.UserPasswordLoginParam;
import com.example.lotterysystem.controller.param.UserRegisterParam;
import com.example.lotterysystem.dao.dataobject.PhoneEncrypt;
import com.example.lotterysystem.dao.dataobject.UserDO;
import com.example.lotterysystem.dao.mapper.UserMapper;
import com.example.lotterysystem.service.UserService;
import com.example.lotterysystem.service.VerificationCodeService;
import com.example.lotterysystem.service.dto.UserDTO;
import com.example.lotterysystem.service.dto.UserLoginDTO;
import com.example.lotterysystem.service.dto.UserRegisterDTO;
import com.example.lotterysystem.service.enums.UserIdentityEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private VerificationCodeService verificationCodeService;

    private static final String JWT_USER_ID = "id";
    private static final String JWT_USER_NAME = "name";
    private static final String JWT_USER_IDENTIFY = "identify";

    /**
     * 实现注册功能
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        // 参数校验
        checkRegisterInfo(param);
        // 构造 dao 层请求
        UserDO userDO = new UserDO();
        userDO.setUserName(param.getName());
        userDO.setEmail(param.getEmail());
        userDO.setPhoneNumber(new PhoneEncrypt(param.getPhoneNumber()));
        userDO.setIdentity(param.getIdentity());
        if (StringUtils.hasText(param.getPassword())) {
            userDO.setPassword(SecurityUtil.encipherPassword(param.getPassword()));
        }
        // 将注册信息存入数据库
        userMapper.insert(userDO);

        // 构造返回结果
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        // 返回结果
        return userRegisterDTO;
    }

    /**
     * 用户信息列表
     * @param identity
     * @return
     */
    @Override
    public List<UserDTO> findUserBaseInfoList(String identity) {
        // 参数校验
        if (null != identity && UserIdentityEnum.forName(identity) == null) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        // 查询
        List<UserDO> userDOList = userMapper.selectByIdentity(identity);
        // 构造响应并返回
        return userDOList.stream()
                .map(userDO -> {
                    UserDTO userDTO = new UserDTO();
                    userDTO.setUserId(userDO.getId());
                    userDTO.setUserName(userDO.getUserName());
                    userDTO.setEmail(userDO.getEmail());
                    userDTO.setPassword(userDO.getPassword());
                    userDTO.setPhoneNumber(userDO.getPhoneNumber().getPhone());
                    userDTO.setIdentity(UserIdentityEnum.valueOf(userDO.getIdentity()));
                    return userDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 用户登录
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO = null;
        // 登录
        if (param instanceof UserPasswordLoginParam) {
            // 密码登录
            userLoginDTO = loginByPassword((UserPasswordLoginParam) param);
        } else if (param instanceof ShortMessageLoginParam) {
            // 短信登录
            userLoginDTO = loginByShortMessage((ShortMessageLoginParam) param);
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_ERROR);
        }
        return userLoginDTO;
    }

    /**
     * 短信登录
     * @param param
     * @return
     */
    private UserLoginDTO loginByShortMessage(ShortMessageLoginParam param) {
        // 手机号校验
        if (!RegexUtil.checkMobile(param.getLoginMobile())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        // 获取用户信息
        UserDO userDO = userMapper.selectByPhoneNumber(
                new PhoneEncrypt(param.getLoginMobile()));
        if (null == userDO) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        }
        // 校验身份信息
        if (StringUtils.hasText(param.getMandatoryIdentity())
                && !param.getMandatoryIdentity()
                        .equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // 验证码校验
        String code = verificationCodeService.getVerificationCode(
                param.getLoginMobile()); // 获取存储的验证码
        if (!param.getVerificationCode().equals(code)) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        // 生成 JWT 令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JWT_USER_ID, userDO.getId());
        claims.put(JWT_USER_NAME, userDO.getUserName());
        claims.put(JWT_USER_IDENTIFY, userDO.getIdentity());
        String token = JWTUtils.genToken(claims);
        // 构造响应并返回
        if (null == token) {
            throw new ServiceException(ServiceErrorCodeConstants.JWT_TOKEN_ACQUIRE_ERROR);
        }
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 密码登录
     * @param param
     * @return
     */
    private UserLoginDTO loginByPassword(UserPasswordLoginParam param) {
        UserDO userDO = null;
        // 判断使用手机号登录还是邮箱登录
        if (RegexUtil.checkMail(param.getLoginName())) { // 邮箱登录
            // 根据邮箱查询
            userDO = userMapper.selectByEmail(param.getLoginName());
        } else if (RegexUtil.checkMobile(param.getLoginName())) { // 根据手机号查询
            // 手机号查询
            userDO = userMapper.selectByPhoneNumber(new PhoneEncrypt(param.getLoginName()));
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_ERROR);
        }

        if (null == userDO) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        } else if (StringUtils.hasText(param.getMandatoryIdentity())
                && !param.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            // 校验身份信息
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!SecurityUtil.verifyPassword(param.getPassword(), userDO.getPassword())) {
            // 校验密码
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 生成 JWT 令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JWT_USER_ID, userDO.getId());
        claims.put(JWT_USER_NAME, userDO.getUserName());
        claims.put(JWT_USER_IDENTIFY, userDO.getIdentity());
        String token = JWTUtils.genToken(claims);

        // 构造响应并返回
        if (null == token) {
            throw new ServiceException(ServiceErrorCodeConstants.JWT_TOKEN_ACQUIRE_ERROR);
        }
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }


    /**
     * 校验注册信息是否符合要求
     * @param param
     */
    private void checkRegisterInfo(UserRegisterParam param) {
        /**
         *  传入参数是否为 null
         */
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }

        /**
         * 用户邮箱是否符合要求
         * xxx@xxx.xxx
         */
        if (!RegexUtil.checkMail(param.getEmail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIN_ERROR);
        }

        /**
         * 用户手机号是否符合要求
         **/
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        /**
         * 用户身份信息校验
         */
        if (null == UserIdentityEnum.forName(param.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        /**
         * 管理员用户密码校验
         * 管理员用户密码必须存在 且 密码长度至少为 6 位
         */
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && (!StringUtils.hasLength(param.getPassword())
                        || !RegexUtil.checkPassword(param.getPassword()))) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        /**
         * 用户邮箱是否已被使用
         */
        if (checkMainUsed(param.getEmail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        /**
         * 用户手机号是否已存在
         */
        if (checkPhoneUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    /**
     * 用户手机号是否已被使用
     * @param phoneNumber
     * @return
     */
    private boolean checkPhoneUsed(String phoneNumber) {
        return userMapper.countByPhone(new PhoneEncrypt(phoneNumber)) > 0;
    }

    /**
     * 用户邮箱是否已被使用
     * @param mail
     * @return
     */
    private boolean  checkMainUsed(String mail) {
        return userMapper.countByEmail(mail) > 0;
    }
}
